/** @file

  Copyright (c) 2004  - 2014, Intel Corporation. All rights reserved.<BR>
                                                                                   
  SPDX-License-Identifier: BSD-2-Clause-Patent

                                                                                   


Module Name:


    SmmScriptSave.c

Abstract:

    ScriptTableSave module at run time

--*/

#include "SmmScriptSave.h"

//
// internal functions
//

EFI_STATUS
BootScriptIoWrite  (
  IN EFI_SMM_SCRIPT_TABLE     *ScriptTable,
  IN VA_LIST                  Marker
  );

EFI_STATUS
BootScriptPciCfgWrite  (
  IN EFI_SMM_SCRIPT_TABLE     *ScriptTable,
  IN VA_LIST                  Marker
  );

VOID
SmmCopyMem (
  IN  UINT8    *Destination,
  IN  UINT8    *Source,
  IN  UINTN    ByteCount
  );

//
// Function implementations
//
EFI_STATUS
SmmBootScriptWrite (
  IN OUT EFI_SMM_SCRIPT_TABLE    *ScriptTable,
  IN UINTN                       Type,
  IN UINT16                      OpCode,
  ...
  )
{
  EFI_STATUS    Status;
  VA_LIST       Marker;

  if (ScriptTable == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Build script according to opcode
  //
  switch ( OpCode ) {

    case EFI_BOOT_SCRIPT_IO_WRITE_OPCODE:
      VA_START(Marker, OpCode);
      Status = BootScriptIoWrite (ScriptTable, Marker);
      VA_END(Marker);
      break;

    case EFI_BOOT_SCRIPT_PCI_CONFIG_WRITE_OPCODE:
      VA_START(Marker, OpCode);
      Status = BootScriptPciCfgWrite(ScriptTable, Marker);
      VA_END(Marker);
      break;

    default:
      Status = EFI_SUCCESS;
      break;
  }

  return Status;
}


EFI_STATUS
SmmBootScriptCreateTable (
  IN OUT EFI_SMM_SCRIPT_TABLE    *ScriptTable,
  IN UINTN                       Type
  )
{
  BOOT_SCRIPT_POINTERS          Script;
  UINT8                         *Buffer;

  if (ScriptTable == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Buffer = (UINT8*) ((UINTN)(*ScriptTable));

  //
  // Fill Table Header
  //
  Script.Raw = Buffer;
  Script.TableInfo->OpCode      = EFI_BOOT_SCRIPT_TABLE_OPCODE;
  Script.TableInfo->Length      = sizeof(EFI_BOOT_SCRIPT_TABLE_HEADER);
  Script.TableInfo->TableLength = sizeof(EFI_BOOT_SCRIPT_TABLE_HEADER);

  //
  // Update current table pointer
  //
  *ScriptTable = *ScriptTable + sizeof(EFI_BOOT_SCRIPT_TABLE_HEADER);
  return EFI_SUCCESS;
}


EFI_STATUS
SmmBootScriptCloseTable (
  IN EFI_SMM_SCRIPT_TABLE        ScriptTableBase,
  IN EFI_SMM_SCRIPT_TABLE        ScriptTablePtr,
  IN UINTN                       Type
  )
{
  BOOT_SCRIPT_POINTERS    Script;

  //
  // Add final "termination" node to script table
  //
  Script.Raw               = (UINT8*) ((UINTN)ScriptTablePtr);
  Script.Terminate->OpCode = EFI_BOOT_SCRIPT_TERMINATE_OPCODE;
  Script.Terminate->Length = sizeof (EFI_BOOT_SCRIPT_TERMINATE);
  ScriptTablePtr          += sizeof (EFI_BOOT_SCRIPT_TERMINATE);


  //
  // Update Table Header
  //
  Script.Raw                    = (UINT8*) ((UINTN)ScriptTableBase);
  Script.TableInfo->OpCode      = EFI_BOOT_SCRIPT_TABLE_OPCODE;
  Script.TableInfo->Length      = sizeof (EFI_BOOT_SCRIPT_TABLE_HEADER);
  Script.TableInfo->TableLength = (UINT32)(ScriptTablePtr - ScriptTableBase);

  return EFI_SUCCESS;
}


EFI_STATUS
BootScriptIoWrite  (
  IN EFI_SMM_SCRIPT_TABLE     *ScriptTable,
  IN VA_LIST                  Marker
  )
{
  BOOT_SCRIPT_POINTERS    Script;
  EFI_BOOT_SCRIPT_WIDTH   Width;
  UINTN                   Address;
  UINTN                   Count;
  UINT8                   *Buffer;
  UINTN                   NodeLength;
  UINT8                   WidthInByte;

  Width     = VA_ARG(Marker, EFI_BOOT_SCRIPT_WIDTH);
  Address   = VA_ARG(Marker, UINTN);
  Count     = VA_ARG(Marker, UINTN);
  Buffer    = VA_ARG(Marker, UINT8*);

  WidthInByte = (UINT8)(0x01 << (Width & 0x03));
  Script.Raw  = (UINT8*) ((UINTN)(*ScriptTable));
  NodeLength  = sizeof (EFI_BOOT_SCRIPT_IO_WRITE) + (WidthInByte * Count);

  //
  // Build script data
  //
  Script.IoWrite->OpCode  = EFI_BOOT_SCRIPT_IO_WRITE_OPCODE;
  Script.IoWrite->Length  = (UINT8)(NodeLength);
  Script.IoWrite->Width   = Width;
  Script.IoWrite->Address = Address;
  Script.IoWrite->Count   = (UINT32)Count;
  SmmCopyMem (
    (UINT8*)(Script.Raw + sizeof (EFI_BOOT_SCRIPT_IO_WRITE)),
    Buffer,
    WidthInByte * Count
    );

  //
  // Update Script table pointer
  //
  *ScriptTable = *ScriptTable + NodeLength;
  return EFI_SUCCESS;
}


EFI_STATUS
BootScriptPciCfgWrite  (
  IN EFI_SMM_SCRIPT_TABLE        *ScriptTable,
  IN VA_LIST                     Marker
  )
{
  BOOT_SCRIPT_POINTERS    Script;
  EFI_BOOT_SCRIPT_WIDTH   Width;
  UINT64                  Address;
  UINTN                   Count;
  UINT8                   *Buffer;
  UINTN                   NodeLength;
  UINT8                   WidthInByte;

  Width     = VA_ARG(Marker, EFI_BOOT_SCRIPT_WIDTH);
  Address   = VA_ARG(Marker, UINT64);
  Count     = VA_ARG(Marker, UINTN);
  Buffer    = VA_ARG(Marker, UINT8*);

  WidthInByte = (UINT8)(0x01 << (Width & 0x03));
  Script.Raw  = (UINT8*) ((UINTN)(*ScriptTable));
  NodeLength  = sizeof (EFI_BOOT_SCRIPT_PCI_CONFIG_WRITE) + (WidthInByte * Count);

  //
  // Build script data
  //
  Script.PciWrite->OpCode  = EFI_BOOT_SCRIPT_PCI_CONFIG_WRITE_OPCODE;
  Script.PciWrite->Length  = (UINT8)(NodeLength);
  Script.PciWrite->Width   = Width;
  Script.PciWrite->Address = Address;
  Script.PciWrite->Count   = (UINT32)Count;
  SmmCopyMem (
    (UINT8*)(Script.Raw + sizeof (EFI_BOOT_SCRIPT_PCI_CONFIG_WRITE)),
    Buffer,
    WidthInByte * Count
    );

  //
  // Update Script table pointer
  //
  *ScriptTable = *ScriptTable + NodeLength;
  return EFI_SUCCESS;
}

VOID
SmmCopyMem (
  IN  UINT8    *Destination,
  IN  UINT8    *Source,
  IN  UINTN    ByteCount
  )
{
  UINTN   Index;

  for (Index = 0; Index < ByteCount; Index++, Destination++, Source++) {
    *Destination = *Source;
  }
}
